package com.jiamingZhao.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkNotNull;

public final class ListKit {

    private ListKit() {
        super();
    }

    public static <E> int findIndex(List<E> list, Predicate<E> predicate) {
        for (int i = 0; i < list.size(); i++) {
            if (predicate.test(list.get(i))) {
                return i;
            }
        }
        return -1;
    }

    public static <E> Optional<E> find(List<E> list, Predicate<E> predicate) {
        for (E e : list) {
            if (predicate.test(e)) {
                return Optional.ofNullable(e);
            }
        }
        return Optional.empty();
    }

    /**
     * Title: findFirstRepeatElemnt
     * Description: 获取第一个重复的元素
     * Date: 2020年4月14日
     *
     * @param <E>
     * @param list
     * @param operator
     * @return
     */
    public static <E> Optional<E> findFirstRepeatElemnt(List<E> list, Function<E, String> operator) {
        if (ObjectUtil.isEmpty(list)) {
            return Optional.empty();
        }
        HashSet<String> set = Sets.newHashSetWithExpectedSize(list.size());
        for (E e : list) {
            String key = operator.apply(e);
            if (set.contains(key)) {
                return Optional.ofNullable(e);
            }
            set.add(key);
        }
        return Optional.empty();
    }

    /**
     * Title: existRepeatElemnt
     * Description: 判断是否存在重复的元素
     * Date: 2020年4月14日
     *
     * @param <E>
     * @param list
     * @param operator
     * @return
     */
    public static <E> boolean existRepeatElemnt(List<E> list, Function<E, String> operator) {
        if (ObjectUtil.isEmpty(list)) {
            return false;
        }
        HashSet<String> set = Sets.newHashSetWithExpectedSize(list.size());
        for (E e : list) {
            String key = operator.apply(e);
            if (set.contains(key)) {
                return true;
            }
            set.add(key);
        }
        return true;
    }

    /**
     * Title: filter
     * Description: 对List进行过滤
     * Date: 2020年2月19日
     *
     * @param <E>
     * @param list
     * @param predicate 过滤函数
     * @return
     */
    public static <E> List<E> filter(List<E> list, Predicate<E> predicate) {
        checkNotNull(predicate, "predicate cannot be null");
        if (ObjectUtil.isEmpty(list)) {
            return list;
        }
        return list.stream().filter(predicate).collect(Collectors.toList());
    }

    /**
     * Title: convert
     * Description: 转换list对象为指定类型
     * 2020年2月4日
     *
     * @param <E>
     * @param <R>
     * @param list
     * @param operator
     * @return
     */
    public static <E, R> List<R> convert(List<E> list, Function<E, R> operator) {
        return getField(list, operator);
    }

    public static <E, R> List<R> convert(List<E> list, BiFunction<E, Integer, R> operator) {
        if (ObjectUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        List<R> resultList = new ArrayList<>(list.size());
        forEach(list, (item, index) -> {
            final R value = operator.apply(item, index);
            if (Objects.isNull(value)) {
                return;
            }
            resultList.add(value);
        });
        return resultList;
    }

    /**
     * Title: getField
     * Description: 获取指定字段，跳过null
     * Date: 2020年4月29日
     *
     * @param <E>
     * @param <R>
     * @param list
     * @param operator
     * @return
     */
    public static <E, R> List<R> getField(List<E> list, Function<E, R> operator) {
        return getField(list, operator, true);
    }

    public static <E, R> List<R> getField(List<E> list, Function<E, R> operator, boolean ignoreNull) {
        List<R> fields = new ArrayList<>(list.size());
        list.forEach(item -> {
            final R value = operator.apply(item);
            if (Objects.isNull(value) && ignoreNull) {
                return;
            }
            fields.add(value);
        });
        return fields;
    }

    public static <E, R> List<R> getDistinctField(List<E> list, Function<E, R> operator) {
        List<R> fields = getField(list, operator);
        return distinct(fields, Function.identity());
    }

    public static <E, R> List<R> getFieldFlat(List<E> list, Function<E, List<R>> operator) {
        return ListKit.getField(list, operator).stream().flatMap(java.util.Collection::stream).collect(Collectors.toList());
    }

    /**
     * Title: getFieldSet
     * Description:获取指定字段集合，跳过null
     * Date: 2020年4月29日
     *
     * @param <E>
     * @param <R>
     * @param list
     * @param operator
     * @return
     */
    public static <E, R> Set<R> getFieldSet(List<E> list, Function<E, R> operator) {
        Set<R> fieldSet = Sets.newHashSetWithExpectedSize(list.size());
        if (CollectionUtil.isEmpty(list)) {
            return fieldSet;
        }
        list.forEach(item -> {
            final R value = operator.apply(item);
            if (Objects.isNull(value)) {
                return;
            }
            fieldSet.add(value);
        });
        return fieldSet;
    }

    /**
     * Title: getFieldStr
     * Description: 将list中的字段使用，连接成字符串,跳过null和空字符串
     * Date: 2020年3月3日
     *
     * @param <E>
     * @param list
     * @param operator
     * @return
     */
    public static <E> String getFieldStr(List<E> list, Function<E, String> operator) {
        return getFieldStr(list, operator, ",");
    }

    public static <E> String getFieldStr(List<E> list, Function<E, String> operator, String split) {
        if (ObjectUtil.isEmpty(list)) {
            return "";
        }
        if (StrUtil.isBlank(split)) {
            split = ",";
        }
        StringBuilder sb = new StringBuilder();
        for (E item : list) {
            final String value = operator.apply(item);
            if (StrUtil.isBlank(value)) {
                continue;
            }
            sb.append(value).append(split);
        }
        if (sb.length() == 0) {
            return "";
        }
        return sb.delete(sb.length() - 1, sb.length()).toString();
    }

    public static <E> String getFieldStr(Collection<E> list, Function<E, String> operator, String split) {
        if (ObjectUtil.isEmpty(list)) {
            return "";
        }
        if (StrUtil.isBlank(split)) {
            split = ",";
        }
        StringBuilder sb = new StringBuilder();
        for (E item : list) {
            final String value = operator.apply(item);
            if (StrUtil.isBlank(value)) {
                continue;
            }
            sb.append(value).append(split);
        }
        if (sb.length() == 0) {
            return "";
        }
        return sb.delete(sb.length() - 1, sb.length()).toString();
    }

    /**
     * Title: convertToMap
     * Description: 转换list为map，key值跳过空字符串和null
     * Date: 2020年4月29日
     */
    public static <K, E> Map<K, E> convertToMap(List<E> list, Function<E, K> operator) {
        if (ObjectUtil.isEmpty(list)) {
            return Maps.newHashMap();
        }
        Map<K, E> resultMap = Maps.newHashMapWithExpectedSize(list.size());
        list.forEach(item -> {
            final K key = operator.apply(item);
            if (ObjectUtil.isEmpty(key)) {
                return;
            }
            resultMap.put(key, item);
        });
        return resultMap;
    }

    public static <K, V, E> Map<K, V> convertToMap(List<E> list, Function<E, K> keyFunction, Function<E, V> valueFunction) {
        if (ObjectUtil.isEmpty(list)) {
            return Maps.newHashMap();
        }
        Map<K, V> resultMap = Maps.newHashMapWithExpectedSize(list.size());
        list.forEach(item -> {
            resultMap.put(keyFunction.apply(item), valueFunction.apply(item));
        });
        return resultMap;
    }

    public static <E, K> Map<K, E> convert2Map(List<E> list, Function<E, K> operator) {
        if (ObjectUtil.isEmpty(list)) {
            return Maps.newHashMap();
        }
        Map<K, E> resultMap = Maps.newHashMapWithExpectedSize(list.size());
        list.forEach(item -> {
            final K key = operator.apply(item);
            resultMap.put(key, item);
        });
        return resultMap;
    }

    /**
     * Title: mergeToMap
     * Description: 合并相同项
     * Date: 2020年2月19日
     *
     * @param <K>           键的类型
     * @param <E>           对象的类型
     * @param list          操作的集合
     * @param keyFunction   获取key的函数
     * @param mergeFunction 合并相同key项的行数， 参数 当前项，已存在的相同项(可能为空)
     * @reEurn
     */
    public static <K, E> List<E> merge(List<E> list, Function<E, K> keyFunction, BiFunction<E, E, E> mergeFunction) {
        Map<K, E> map = mergeToMap(list, keyFunction, (a, b) -> {
            if (Objects.isNull(b)) {
                return a;
            } else {
                return mergeFunction.apply(a, b);
            }
        });
        return Lists.newArrayList(map.values());
    }

    public static <K, E> List<E> merge(List<E> list, Function<E, K> keyFunction, BiFunction<E, E, E> mergeFunction, Function<E, E> initValFunction) {
        Map<K, E> map = merge2Map(list, keyFunction, mergeFunction, initValFunction);
        return Lists.newArrayList(map.values());
    }

    /**
     * Title: mergeToMap
     * Description: 合并相同项转换为map
     * Date: 2020年2月19日
     *
     * @param <K>           键的类型
     * @param <E>           对象的类型
     * @param list          操作的集合
     * @param keyFunction   获取key的函数
     * @param mergeFunction 合并相同key项的行数， 参数 当前项，已存在的相同项(可能为空)
     * @return
     */
    public static <K, E, R> Map<K, R> mergeToMap(List<E> list, Function<E, K> keyFunction, BiFunction<E, R, R> mergeFunction) {
        Map<K, R> map = Maps.newLinkedHashMap();
        list.forEach(item -> {
            K key = keyFunction.apply(item);
            map.compute(key, (k, target) -> mergeFunction.apply(item, target));
        });
        return map;
    }

    /**
     * Title: merge2Map
     * Description: 合并相同项转换为map
     * Date: 2020年5月29日
     *
     * @param <K>
     * @param <E>
     * @param <R>
     * @param list            数据源
     * @param keyFunction     提供key的函数
     * @param mergeFunction   做合并操作的函数
     * @param initValFunction 当key对应的value不存在时，用于初始化的函数，走这个function就不会走mergeFunction
     * @return
     */
    public static <K, E, R> Map<K, R> merge2Map(List<E> list, Function<E, K> keyFunction, BiFunction<E, R, R> mergeFunction, Function<E, R> initValFunction) {
        Map<K, R> map = Maps.newLinkedHashMap();
        list.forEach(item -> {
            K key = keyFunction.apply(item);
            R target = map.get(key);
            if (Objects.isNull(target)) {
                target = initValFunction.apply(item);
                map.put(key, target);
            } else {
                map.put(key, mergeFunction.apply(item, target));
            }
        });
        return map;
    }

    /**
     * Title: mergeToMap
     * Description:
     * Date: 2020年5月29日
     *
     * @param <K>
     * @param <E>
     * @param <R>
     * @param list          数据源
     * @param keyFunction   提供key的函数
     * @param mergeFunction 做合并操作的函数
     * @param initValue     当key对应的value不存在时，作为初始化的值，注意：此处应传入基本类型，否则可能会出现整个Map存放的是同一个值，如需构造对象，请使用 {@link #merge2Map(List, Function, BiFunction, Supplier)} 或
     *                      {@link #merge2Map(List, Function, BiFunction, Function)}
     * @return
     */
    public static <K, E, R> Map<K, R> mergeToMap(List<E> list, Function<E, K> keyFunction, BiFunction<E, R, R> mergeFunction, R initValue) {
        Map<K, R> map = Maps.newLinkedHashMap();
        list.forEach(item -> {
            K key = keyFunction.apply(item);
            R target = map.get(key);
            if (Objects.isNull(target)) {
                target = initValue;
            }
            map.put(key, mergeFunction.apply(item, target));
        });
        return map;
    }

    /**
     * Title: mergeToMap
     * Description:
     * Date: 2020年5月29日
     *
     * @param <K>
     * @param <E>
     * @param <R>
     * @param list            数据源
     * @param keyFunction     提供key的函数
     * @param mergeFunction   做合并操作的函数
     * @param initValFunction 当key对应的value不存在时，用于初始化的函数
     * @return
     */
    public static <K, E, R> Map<K, R> merge2Map(List<E> list, Function<E, K> keyFunction, BiFunction<E, R, R> mergeFunction, Supplier<R> initValFunction) {
        Map<K, R> map = Maps.newLinkedHashMap();
        list.forEach(item -> {
            K key = keyFunction.apply(item);
            R target = map.get(key);
            if (Objects.isNull(target)) {
                target = initValFunction.get();
            }
            map.put(key, mergeFunction.apply(item, target));
        });
        return map;
    }

    /**
     * Title: groupToMap
     * Description: 根据指定规则将list聚合为Map
     * Date: 2020年3月18日
     *
     * @param <K>
     * @param <E>
     * @param list
     * @param keyFunction
     * @return
     */
    public static <K, E> Map<K, List<E>> groupToMap(List<E> list, Function<E, K> keyFunction) {
        Map<K, List<E>> map = Maps.newLinkedHashMap();
        list.forEach(item -> {
            K key = keyFunction.apply(item);
            List<E> target = map.get(key);
            if (Objects.isNull(target)) {
                target = Lists.newLinkedList();
                map.put(key, target);
            }
            target.add(item);
        });
        return map;
    }

    public static <K, E> Map<K, List<E>> group(List<E> list, Function<E, K> keyFunction) {
        return groupToMap(list, keyFunction);
    }

    public static <K, E, R> Map<K, List<R>> groupToMap(List<E> list, Function<E, K> keyFunction, Function<E, R> valueFunction) {
        Map<K, List<R>> map = Maps.newLinkedHashMap();
        list.forEach(item -> {
            K key = keyFunction.apply(item);
            List<R> target = map.get(key);
            if (Objects.isNull(target)) {
                target = Lists.newLinkedList();
                map.put(key, target);
            }
            R value = valueFunction.apply(item);
            if (Objects.nonNull(value)) {
                target.add(value);
            }
        });
        return map;
    }

    /**
     * Title: distinct
     * Description: 过滤重复元素
     * 2020年6月24日
     */
    public static <K, E> List<E> distinct(List<E> list, Function<E, K> keyFunction) {
        Map<K, E> linkedMap = Maps.newLinkedHashMap();
        for (E item : list) {
            K key = keyFunction.apply(item);
            linkedMap.put(key, item);
        }
        return Lists.newArrayList(linkedMap.values());
    }

    /**
     * Title:setGroup <br>
     * Description:1对多设置指
     *
     * @param list1
     * @param key1Function
     * @param list2
     * @param key2Function
     * @param setConsmer
     * @return void
     * @author 肖晓霖
     * @date 2020-11-19 20:09
     */
    public static <E1, E2> void setGroup(List<E1> list1, Function<E1, String> key1Function, List<E2> list2, Function<E2, String> key2Function, BiConsumer<E1, List<E2>> setConsmer) {
        if (!ObjectUtil.isAllNotEmpty(list1, list2)) {
            return;
        }
        Map<String, List<E2>> list2Map = groupToMap(list2, key2Function);
        for (E1 e1 : list1) {
            String key = key1Function.apply(e1);
            List<E2> l2 = list2Map.get(key);
            if (ObjectUtil.isNotEmpty(l2)) {
                setConsmer.accept(e1, l2);
            }
        }
    }

    /**
     * Title:set <br>
     * Description:1对1设置值
     *
     * @param list1
     * @param key1Function
     * @param list2
     * @param key2Function
     * @param setConsmer
     * @return void
     * @author 肖晓霖
     * @date 2020-11-19 20:09
     */
    public static <E1, E2> void set(List<E1> list1, Function<E1, String> key1Function, List<E2> list2, Function<E2, String> key2Function, BiConsumer<E1, E2> setConsmer) {
        if (!ObjectUtil.isAllNotEmpty(list1, list2)) {
            return;
        }
        Map<String, E2> list2Map = convert2Map(list2, key2Function);
        for (E1 e1 : list1) {
            String key = key1Function.apply(e1);
            E2 e2 = list2Map.get(key);
            if (ObjectUtil.isNotEmpty(e2)) {
                setConsmer.accept(e1, e2);
            }
        }
    }

    public static <E> void forEach(List<E> list, BiConsumer<E, Integer> consumer) {
        if (ObjectUtil.isEmpty(list)) {
            return;
        }
        for (int i = 0, size = list.size(); i < size; i++) {
            consumer.accept(list.get(i), i);
        }
    }

    public static <K, E, R> List<R> groupAndConvert(List<E> list, Function<E, K> keyFunction, BiFunction<K, List<E>, R> operator) {
        return MapKit.convertToList(groupToMap(list, keyFunction), operator);
    }

    public static <K, E, R> Map<K, R> groupAndConvertMap(List<E> list, Function<E, K> keyFunction, BiFunction<K, List<E>, R> operator) {
        return MapKit.convert(groupToMap(list, keyFunction), operator);
    }


    public static <T> T findFirst(List<T> list, Predicate<T> predicate) {
        if (ObjectUtil.isEmpty(list)) {
            return null;
        }
        return list.stream().filter(predicate).findFirst().orElse(null);
    }

    /**
     * 根据一系列谓词从列表中找到最匹配的元素。
     * 此方法接受一个元素列表和一个谓词列表作为输入。谓词列表包含了一系列用于测试元素的条件。
     * 方法的目的是在元素列表中找到一个元素，该元素满足所有给定的谓词条件。如果存在多个满足所有条件的元素，
     * 则返回第一个找到的元素。如果没有元素满足所有条件，则返回null。
     *
     * @param <T>        被搜索元素和谓词操作的泛型类型。
     * @param list       包含待搜索元素的列表。
     * @param predicates 一个包含用于测试元素的谓词的列表。
     * @return 列表中满足所有谓词条件的第一个元素，如果不存在则返回null。
     */
    public static <T> T findBestMatch(List<T> list, List<Predicate<T>> predicates) {
        if (ObjectUtil.isEmpty(list)) {
            return null;
        }
        for (Predicate<T> predicate : predicates) {
            T t = list.stream().filter(predicate).findFirst().orElse(null);
            if (Objects.nonNull(t)) {
                return t;
            }
        }
        return null;
    }

    public static <T> T findBestMatch(List<T> list, Predicate<T> p1) {
        return findBestMatch(list, Collections.singletonList(p1));
    }

    @SafeVarargs
    public static <T> T findBestMatch(List<T> list, final Predicate<T>... predicates) {
        return findBestMatch(list, Arrays.asList(predicates));
    }

    public static <E> E findBestMatch(List<E> list, Predicate<E> p1, Predicate<E> p2) {
        return findBestMatch(list, Arrays.asList(p1, p2));
    }

    public static <T> T findBestMatch(List<T> list, Predicate<T> p1, Predicate<T> p2, Predicate<T> p3) {
        return findBestMatch(list, Arrays.asList(p1, p2, p3));
    }

    public static <T, C extends Comparable<C>> Set<T> intersectionDistinct(List<List<T>> lists, Function<T, C> operator) {
        Map<C, T> keyMap = new HashMap<>();
        lists.stream().flatMap(Collection::stream).forEach(e -> {
            keyMap.put(operator.apply(e), e);
        });
        final Set<C> keyResult = new HashSet<>(keyMap.keySet());
        if (lists.stream().anyMatch(Collection::isEmpty)) {
            return new HashSet<>();
        }
        for (List<T> list : lists) {
            keyResult.retainAll(ListKit.convert(list, operator));
        }
        return keyResult.stream().map(keyMap::get).collect(Collectors.toSet());
    }

    @SafeVarargs
    public static <T> List<T> mergetList(Collection<T>... lists){
        if (ObjectUtil.isEmpty(lists)) {
            return Lists.newArrayList();
        }
        return Arrays.stream(lists).filter(CollUtil::isNotEmpty).flatMap(Collection::stream)
                .toList();
    }
}
